<?php

namespace System\Data;

/**
 * Description of Mapped
 *
 * @author elton.schivei
 */
class Mapped extends \System\Types\Object
{

    /**
     * @var \ReflectionClass
     */
    private $_class;
    private $_table;
    private $_columns;
    private $_classConfig;

    public function __construct(\System\Types\Reflections\ReflectionAssembly $class)
    {
        $this->_class = $class->Reflection();

        $this->mountEntity();

        $this->mountProperties();

        $this->MakeEvaldAbstractionClass();
    }

    public function MakeEvaldAbstractionClass()
    {
        $className = $this->_class->getShortName();
        $superClass = $this->_class->getName();

        $evalution = <<<"CLASS"
namespace System\Data
{
    final class T{$className} extends {$superClass}
    {
        public function __construct(IDataResult \$result, \System\Types\Integer \$index)
        {
            parent::__construct();

            if(\$result)
            {
                \$unique = \$result->FetchUnique(\$index);

                if(\$unique)
                {
                    foreach(\$unique as \$key => \$value)
                    {
                        \$k = new \System\Types\String(\$key);

                        \$field = '_' . \$k->Camelize();

                        \$this->{\$field} = \$value;
                    }

                    return;
                }

                System\Core\Errors\Error::ShowErr("Não foi possível obter dados com o ResultSet informado.");
            }

            System\Core\Errors\Error::ShowErr("Não foi possível identificar o ResultSet informado.");
        }
    }
}
CLASS;

        $evalution = new \System\Types\String($evalution);

        \evald($evalution);
    }

    public function __get($var)
    {
        if ($var == 'Table')
            return $this->_table;

        return $this->_columns->{$var};
    }

    private function mountEntity()
    {
        $doc = \str_replace("\n", ' ', \str_replace("\r", ' ', $this->_class->getDocComment()));

        $matches = array();

        \preg_match('/\[EntityMap\((.+)\)\]/i', $doc, $matches);

        if (!\count($matches))
            \System\Core\Errors\Error::ShowErr("A classe informada não é mapeável ou não está com a configuração de mapeamento configurada corretamente.");

        $c = \System\Data\AEntity::GetTheName();

        if ($this->_class->isAbstract() || $this->_class->isFinal() ||
                !$this->_class->isInstantiable() || !$this->_class->isSubclassOf($c))
            \System\Core\Errors\Error::ShowErr("A classe informada não pode ser final ou abstrata. A classe informada deve aceitar instâncias públicas e extender a classe abastrata \"{$c}\".");

        $classConfig = (object) array();

        $classMapOptions = \explode(',', $matches[1]);

        foreach ($classMapOptions as $opt)
        {
            $var = \explode(':', $opt);

            $var[0] = \trim($var[0]);
            $var[1] = \trim($var[1]);

            $var[0] = '_' . \lcfirst($var[0]);

            $var[1] = \addslashes($var[1]);

            $classConfig->{$var[0]} = null;

            $var[1] = \addslashes($var[1]);

            eval("\$classConfig->{$var[0]} = {$var[1]};");

            if (!empty($classConfig->_table))
                $this->_table = $classConfig->_table;
        }

        $this->_classConfig = $classConfig;

        if (!$this->_table)
            \System\Core\Errors\Error::ShowErr("A classe informada não possui mapeamento com uma tabela válida.");
    }

    public function mountProperties()
    {
        $ps = array();

        if (!empty($this->_classConfig->base))
        {
            $base = new \ReflectionClass($this->_classConfig->base);

            if ($base)
            {
                $ps = $base->getProperties();
            }
        }

        $props = $this->_class->getProperties();

        \array_merge($ps, $props);

        $this->_columns = (object) array();

        /* @var $property \ReflectionProperty */
        foreach ($props as $property)
        {
            $mtchs = array();

            $dc = \str_replace("\n", ' ', \str_replace("\r", ' ', $property->getDocComment()));

            \preg_match('/\[PropertyMap\((.+)\)\]/i', $dc, $mtchs);

            if (!\count($mtchs))
                continue;

            if ($property->isPrivate())
                \System\Core\Errors\Error::ShowErr("A propriedade para mapeamento não pode ser privada.");
            else if ($property->isPublic())
                \System\Core\Errors\Error::ShowErr("A propriedade para mapeamento não pode ser pública.");

            if ($property->isStatic())
                \System\Core\Errors\Error::ShowErr("A propriedade para mapeamento não pode ser estática.");

            $propConfig = (object) array();

            $propMapOptions = \explode(',', $mtchs[1]);

            foreach ($propMapOptions as $prop)
            {
                $var = \explode(':', $prop);

                $var[0] = \trim($var[0]);
                $var[1] = \trim($var[1]);

                $var[0] = '_' . \lcfirst($var[0]);

                $propConfig->{$var[0]} = null;

                $var[1] = \addslashes($var[1]);

                eval("\$classConfig->{$var[0]} = {$var[1]};");
            }

            $this->_columns->{$property->name} = $propConfig;
        }
    }

}
